#ifndef NE_STL_INCLUDENE_STL_BIND_H
#define NE_STL_INCLUDENE_STL_BIND_H

#include "ne_stl_atomic.h"
#include "ne_stl_build.h"
#include "ne_stl_memory.h"
#include "ne_stl_smart_ptr.h"
#include "ne_stl_tuple.h"

namespace nstd {
typedef struct _weak_flag {
} weak_flag;
typedef struct _weak_semantics_supporter {
    _weak_semantics_supporter() { shared_obj_ = nstd::make_shared<nstd::weak_flag>(); }
    inline nstd::weak_ptr<nstd::weak_flag> get_weak_flag() { return shared_obj_; }

private:
    nstd::shared_ptr<nstd::weak_flag> shared_obj_;
} weak_semantics_supporter;

template <typename T>
struct type {
    using value = T;
};
template <typename R>
struct result_traits {
    using type = R;
};
template <typename F>
struct result_traits<type<F>> : result_traits<typename F::result_type> {};
struct argc_placeholders_base {};
template <int32_t index>
struct _argc_placeholder : argc_placeholders_base {};
namespace placeholders {
static _argc_placeholder<1> _1;
static _argc_placeholder<2> _2;
static _argc_placeholder<3> _3;
static _argc_placeholder<4> _4;
static _argc_placeholder<5> _5;
static _argc_placeholder<6> _6;
static _argc_placeholder<7> _7;
static _argc_placeholder<8> _8;
static _argc_placeholder<9> _9;
}  // namespace placeholders
struct tuple_valid_param_count {
    template <typename... TArgs>
    static int32_t count(const nstd::tuple<TArgs...>&) {
        int32_t count = 0;
        get_i<sizeof...(TArgs), TArgs...>(count);
        return count;
    }
    template <int32_t I, typename T, typename... TArgs>
    static void get_i(int32_t& count) {
        if (!std::is_base_of<argc_placeholders_base, T>::value) {
            count += 1;
            get_i<sizeof...(TArgs), TArgs...>(count);
        }
    }
    template <int32_t I>
    static void get_i(int32_t& count) {}
};
template <typename TR, typename TC, typename TF, typename TCaptured>
struct bind_t {
    template <typename R, typename C, typename... TArgs, typename TUP, int32_t... Index_1, typename TUP2, int32_t... Index_2>
    static R bind_t_call_(R (C::*f)(TArgs...),
        C* obj,
        const TUP& tup,
        nstd::int_sequence<Index_1...>,
        const TUP2& tup2,
        nstd::int_sequence<Index_2...>) {
        return (obj->*f)(nstd::tuple_get<Index_1>(tup)..., nstd::tuple_get<Index_2>(tup2)...);
    }
    template <typename R, typename C, typename... TArgs, typename... Ts_1, typename... Ts_2>
    static R bind_t_call(R (C::*f)(TArgs...), C* obj, const nstd::tuple<Ts_1...>& tup, const nstd::tuple<Ts_2...>& tup2) {
        return bind_t_call_(f, obj, tup, nstd::sequence_t<sizeof...(Ts_1)>{}, tup2, nstd::sequence_t<sizeof...(Ts_2)>{});
    }
    template <typename R, typename C, typename... TArgs, typename TUP, int32_t... Index_1, typename TUP2, int32_t... Index_2>
    static R bind_t_call_(R (C::*f)(TArgs...) const,
        C* obj,
        const TUP& tup,
        nstd::int_sequence<Index_1...>,
        const TUP2& tup2,
        nstd::int_sequence<Index_2...>) {
        return (obj->*f)(nstd::tuple_get<Index_1>(tup)..., nstd::tuple_get<Index_2>(tup2)...);
    }
    template <typename R, typename C, typename... TArgs, typename... Ts_1, typename... Ts_2>
    static R bind_t_call(R (C::*f)(TArgs...) const, C* obj, const nstd::tuple<Ts_1...>& tup, const nstd::tuple<Ts_2...>& tup2) {
        return bind_t_call_(f, obj, tup, nstd::sequence_t<sizeof...(Ts_1)>{}, tup2, nstd::sequence_t<sizeof...(Ts_2)>{});
    }
    bind_t(TF f, TC* obj, const TCaptured& params, const nstd::weak_ptr<nstd::weak_flag>& weak_flag)
        : obj_(obj)
        , functor_(f)
        , weak_flag_(weak_flag)
        , captured_params_(params) {}
    template <typename... TParams>
    inline TR operator()(TParams... params) {
        if (weak_flag_.expired())
            return TR();
        if constexpr (sizeof...(params) > 0) {
            return bind_t_call(functor_, obj_, captured_params_, nstd::make_tuple(params...));
        } else {
            return bind_t_call(functor_, obj_, captured_params_, nstd::tuple<>());
        }
    }
    template <typename... TParams>
    inline TR operator()(TParams... params) const {
        if (weak_flag_.expired())
            return TR();
        if constexpr (sizeof...(params) > 0) {
            return bind_t_call(functor_, obj_, captured_params_, nstd::make_tuple(params...));
        } else {
            return bind_t_call(functor_, obj_, captured_params_, nstd::tuple<>());
        }
    }
    TC* obj_;
    TF functor_;
    nstd::weak_ptr<nstd::weak_flag> weak_flag_;
    TCaptured captured_params_;
};
template <typename R, typename C, typename... Args, typename... Params>
bind_t<R, C, R (C::*)(Args...), nstd::tuple<Params...>> bind(R (C::*f)(Args...), C* obj, Params... params) {
    static_assert(std::is_base_of<nstd::weak_semantics_supporter, C>::value, "nstd::_weak_semantics_supporter should be base of C");
    return bind_t<R, C, R (C::*)(Args...), nstd::tuple<Params...>>(f, obj, nstd::make_tuple(params...), obj->get_weak_flag());
}
template <typename R, typename C, typename... Args, typename... Params>
bind_t<R, C, R (C::*)(Args...) const, nstd::tuple<Params...>> bind(R (C::*f)(Args...) const, C* obj, Params... params) {
    static_assert(std::is_base_of<nstd::weak_semantics_supporter, C>::value, "nstd::_weak_semantics_supporter should be base of C");
    return bind_t<R, C, R (C::*)(Args...) const, nstd::tuple<Params...>>(f, obj, nstd::make_tuple(params...), obj->get_weak_flag());
}

template <typename T>
struct function;
template <typename R, typename... Args>
struct function<R(Args...)> {
private:
    template <typename T>
    struct _function;
    template <typename _R, typename... _Args>
    struct _function<_R(_Args...)> {
        using result_type = typename result_traits<_R>::type;
        using invoke_fn_t = _R (*)(char*, _Args...);
        using construct_fn_t = void (*)(char*, char*);
        using destroy_fn_t = void (*)(char*);
        template <typename Functor>
        static result_type invoke_fn(Functor* fn, _Args... args) {
            return (*fn)(args...);
        }
        template <typename Functor>
        static void construct_fn(Functor* construct_dst, Functor* construct_src) {
            new (construct_dst) Functor(*construct_src);
        }
        template <typename Functor>
        static void destroy_fn(Functor*& f) {
            if (f != nullptr) {
                f->~Functor();
                memory::safe_delete_container_element(reinterpret_cast<char*&>(f));
            }
        }
        using functor_container = nstd::vector<char>;

    public:
        _function()
            : invoke_f(nullptr)
            , construct_f(nullptr)
            , data_ptr(nullptr)
            , data_size(0) {}
        _function(std::nullptr_t)
            : invoke_f(nullptr)
            , construct_f(nullptr)
            , data_ptr(nullptr)
            , data_size(0) {}
        template <typename Functor>
        _function(const Functor& f)
            : invoke_f(reinterpret_cast<invoke_fn_t>(invoke_fn<Functor>))
            , construct_f(reinterpret_cast<construct_fn_t>(construct_fn<Functor>))
            , data_size(sizeof(Functor)) {
            this->data_ptr = nstd::make_shared<vector<char>>();
            this->data_ptr->set_element_delete_function(reinterpret_cast<nstd::element_delete_fun_t<char>>(destroy_fn<Functor>));
            this->data_ptr->resize(data_size);
            this->construct_f((char*)this->data_ptr->data(), reinterpret_cast<char*>(const_cast<Functor*>(&f)));
        }
        _function(const _function& rhs)
            : invoke_f(rhs.invoke_f)
            , construct_f(rhs.construct_f)
            , data_ptr(rhs.data_ptr)
            , data_size(rhs.data_size) {}
        _function& operator=(const _function& rhs) {
            this->invoke_f = rhs.invoke_f;
            this->construct_f = rhs.construct_f;
            this->data_size = rhs.data_size;
            this->data_ptr = rhs.data_ptr;
            return *this;
        }
        template <typename Functor>
        _function& operator=(const Functor& f) {
            this->invoke_f = reinterpret_cast<invoke_fn_t>(invoke_fn<Functor>);
            this->construct_f = reinterpret_cast<construct_fn_t>(construct_fn<Functor>);
            this->data_size = sizeof(Functor);
            this->data_ptr = nstd::make_shared<vector<char>>();
            this->data_ptr->set_element_delete_function(reinterpret_cast<nstd::element_delete_fun_t<char>>(destroy_fn<Functor>));
            this->data_ptr->resize(data_size);
            this->construct_f((char*)this->data_ptr->data(), reinterpret_cast<char*>(const_cast<Functor*>(&f)));
            return *this;
        }
        _function& operator=(std::nullptr_t) {
            this->invoke_f = nullptr;
            this->construct_f = nullptr;
            this->data_ptr = nullptr;
            this->data_size = 0;
            return *this;
        }
        ~_function() {}
        inline bool operator==(std::nullptr_t) const { return !this->operator bool(); }
        inline bool operator!=(std::nullptr_t) const { return this->operator bool(); }
        inline bool operator==(const _function& other) const { return this->data_ptr == other.data_ptr; }
        inline bool operator!=(const _function& other) const { return !this->operator==(other); }
        inline operator bool() const { return this->data_ptr != nullptr; }
        _R operator()(_Args... args) {
            if (this->data_ptr.get() != nullptr)
                return this->invoke_f((char*)this->data_ptr.get()->data(), args...);
            else {
                assert(false);
                return _R();
            }
        }
        _R operator()(_Args... args) const {
            if (this->data_ptr != nullptr)
                return this->invoke_f((char*)this->data_ptr.get()->data(), args...);
            else {
                assert(false);
                return _R();
            }
        }
        invoke_fn_t invoke_f;
        construct_fn_t construct_f;
        nstd::shared_ptr<functor_container> data_ptr;
        std::size_t data_size;
    };

private:
    using deref_notify_t = _function<void()>;
    struct _ref_flag {
        _ref_flag(const deref_notify_t& _deref_notify)
            : deref_notify(_deref_notify) {}
        ~_ref_flag() {
            if (deref_notify != nullptr)
                deref_notify();
        }
        deref_notify_t deref_notify;
    };
    using ref_flag_t = nstd::shared_ptr<_ref_flag>;

public:
    function() { static_assert(nstd::is_all_standard_layout<Args...>::value, "Args should standard layout"); }
    function(std::nullptr_t) { static_assert(nstd::is_all_standard_layout<Args...>::value, "Args should standard layout"); }
    template <typename Functor>
    function(const Functor& f, const deref_notify_t& deref_notify = nullptr)
        : __function_(f)
        , __ref_flag_(nstd::make_shared<_ref_flag>(deref_notify)) {
        static_assert(nstd::is_all_standard_layout<Args...>::value, "Args should standard layout");
    }
    function(const function& rhs)
        : __function_(rhs.__function_)
        , __ref_flag_(rhs.__ref_flag_) {
        static_assert(nstd::is_all_standard_layout<Args...>::value, "Args should standard layout");
    }
    function& operator=(const function& rhs) {
        this->__function_ = rhs.__function_;
        this->__ref_flag_ = rhs.__ref_flag_;
        return *this;
    }
    template <typename Functor>
    function& operator=(const Functor& f) {
        this->__function_ = f;
        this->__ref_flag_ = nullptr;
        return *this;
    }
    function& operator=(std::nullptr_t) {
        this->__function_ = nullptr;
        this->__ref_flag_ = nullptr;
        return *this;
    }
    ~function() { __ref_flag_.reset(); }
    inline bool operator==(std::nullptr_t) const { return !this->operator bool(); }
    inline bool operator!=(std::nullptr_t) const { return this->operator bool(); }
    inline bool operator==(const function& other) const { return this->__function_ == other.__function_; }
    inline bool operator!=(const function& other) const { return !this->operator==(other); }
    inline operator bool() const { return this->__function_ != nullptr; }
    R operator()(Args... args) {
        if (this->__function_ != nullptr)
            return this->__function_(args...);
        else {
            assert(false);
            return R();
        }
    }
    R operator()(Args... args) const {
        if (this->__function_ != nullptr)
            return this->__function_.operator()(args...);
        else {
            assert(false);
            return R();
        }
    }

private:
    _function<R(Args...)> __function_{nullptr};
    ref_flag_t __ref_flag_{nullptr};
};

namespace {
namespace traits {
template <class T>
struct _is_std_function;
}
namespace detail {
template <typename... TArgs>
struct _Has_std_function;
class _Is_std_function {
    template <class TCheck>
    friend struct traits::_is_std_function;
    template <typename... TArgs>
    friend struct _Has_std_function;
    template <class T>
    static constexpr bool _v() {
        return _v((T*)(nullptr));
    }
    template <class TR, class... Args>
    static constexpr bool _v(nstd::function<TR(Args...)>* p) {
        return true;
    }
    template <class T>
    static constexpr bool _v(T* p) {
        return false;
    }
};
template <typename... TArgs>
struct _Has_std_function {};
template <>
struct _Has_std_function<> : nstd::false_type {};
template <typename T, typename... TArgs>
struct _Has_std_function<T, TArgs...>
    : nstd::conditional<_Is_std_function::_v<typename nstd::remove_cr<T>::type>(), nstd::true_type, _Has_std_function<TArgs...>>::type {};
}  // namespace detail
namespace traits {
template <class T>
struct _is_std_function
    : public nstd::conditional<detail::_Is_std_function::_v<typename nstd::remove_cr<T>::type>(), nstd::true_type, nstd::false_type>::type {};

template <class... T>
using _has_std_function = detail::_Has_std_function<T...>;
}  // namespace traits
}  // namespace

template <class T>
using is_std_function = traits::_is_std_function<T>;
template <class... T>
using has_std_function = traits::_has_std_function<T...>;

}  // namespace nstd

#endif  // !NE_STL_INCLUDENE_STL_BIND_H
